﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RayTracer.Kernels
{
    /// <summary>
    /// Beinhaltet den OpenCL Code der Kernel.
    /// </summary>
    partial class KernelRaySplit
    {
        private const string KERNEL_MAIN = "RaySplit";

        /*************
        *  
        *  Eingabe:
        *  material    Das Objektmaterial.                     [i]
        *  rayDir      Die Strahlenrichtung.                   [i]
        *  normal      Die Normale am Schnittpunkt.            [i]
        *  
        * 
        *  Ausgabe:
        *  reflDir     Die Richtung des refl. Strahls.         [i]
        *  refrDir     Die Richtung des refr. Strahls.         [i]
        *  
        *************/
        private const string KERNEL_SOURCE = @"
             kernel void RaySplit(
                global  read_only float16* material,
                global  read_only float4* rayDir,
                global  read_only float4* normal,

                global  write_only float4* reflDir,
                global  write_only float4* refrDir
            )
            {
                int i = get_global_id(0);
              
                float oldRefraction; 
                float newRefraction;
                int vorzeichen;
                float cosRho1;

                float into = dot( normal[i],rayDir[i]);
                 //Prüfe ob die Normale entgegen dem Eingangstrahl gerichtet ist
                if (into < 0.0f) {
                    //Wenn nicht befindet sich der Strahl innerhalb eines Objekts
                    vorzeichen = -1; 
                    oldRefraction = material[i].se;
                    newRefraction = 1.0002926f; //Refraktionsindex von Luft;
                } else {
                    //Andernfalls außerhalb
                    vorzeichen = 1;
                    oldRefraction = rayDir[i].w;
                    newRefraction = material[i].se;
                }	
                //Berechnung der Richtung des Reflektionsstrahls
                if(material[i].sd>0.0f){
                    cosRho1 = dot(vorzeichen*normal[i], rayDir[i]);
                    float4 tempRay = rayDir[i] - (2.0f * cosRho1 * vorzeichen *normal[i]);
                    reflDir[i] = (float4)(tempRay.x, tempRay.y, tempRay.z, rayDir[i].w);
                }
               
                //Berechnung der Richtung des Refraktionsstrahls
                if(material[i].sf> 0){
                 
                    float n;// korrekt wäre  n = oldRefraction/newRefraction das führt jedoch zu einem Verhältnis
                    //größer als 1.0f was zu Ringen führt. Deshalb diese Lösung zur Berechnung des Refraktionsverhältnisses:
                     if ( oldRefraction<=newRefraction){
                        n= oldRefraction/newRefraction;
                    }else{
                         n= newRefraction/oldRefraction;
                    }
                
                    cosRho1 = dot(normal[i],rayDir[i]);
                    float cosRho2 =1.0f -n*n*(1.0f-cosRho1*cosRho1);  
                    if (cosRho2>0.0f){   
                      refrDir[i] = ((n*cosRho1 - vorzeichen*sqrt(cosRho2))*normal[i])-(n*rayDir[i]);
                      refrDir[i].w = rayDir[i].w;
                    }
                     //Alternative Erstellung des Transmissionsstrahls als gerade Weiterführung des Eingangsstrahls.
                     //refrDir[i] = (float4)(-rayDir[i].x,-rayDir[i].y,-rayDir[i].z,rayDir[i].w);
                }
            }
            ";
    }
}
